package accesslog

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"
	"time"

	"baseService/internal/infrastructure/config"
	"baseService/internal/pkg/logger"

	"github.com/gin-gonic/gin"
	"gopkg.in/natefinch/lumberjack.v2"
)

// Entry 访问日志条目
type Entry struct {
	Timestamp    string      `json:"timestamp"`
	Method       string      `json:"method"`
	Path         string      `json:"path"`
	Query        string      `json:"query,omitempty"`
	StatusCode   int         `json:"status_code"`
	ResponseTime string      `json:"response_time"`
	ClientIP     string      `json:"client_ip"`
	UserAgent    string      `json:"user_agent,omitempty"`
	RequestID    string      `json:"request_id,omitempty"`
	UserID       interface{} `json:"user_id,omitempty"`
	RequestSize  int64       `json:"request_size"`
	ResponseSize int         `json:"response_size"`

	// 新增：请求体和响应体（可选）
	RequestBody  string `json:"request_body,omitempty"`
	ResponseBody string `json:"response_body,omitempty"`

	// 新增：错误信息（可选）
	Error string `json:"error,omitempty"`
}

// Config 访问日志配置
type Config struct {
	Enable      bool   `json:"enable"`        // 是否启用访问日志
	IncludeBody bool   `json:"include_body"`  // 是否记录请求体和响应体（环境推导，不需要配置）
	MaxBodySize int    `json:"max_body_size"` // 最大记录的请求体大小（字节）
	Format      string `json:"format"`        // 日志格式：json/text
	Output      string `json:"output"`        // 输出方式：file/console/both
	Filename    string `json:"filename"`      // 日志文件路径
	MaxSize     int    `json:"max_size"`      // 单个日志文件最大大小（MB）
	MaxAge      int    `json:"max_age"`       // 日志文件保留天数
	MaxBackups  int    `json:"max_backups"`   // 最大备份文件数
	Compress    bool   `json:"compress"`      // 是否压缩旧日志文件

	// 跳过配置
	SkipPaths    []string `json:"skip_paths"`    // 跳过记录的路径
	SkipStatuses []int    `json:"skip_statuses"` // 跳过记录的状态码

	// 敏感信息过滤
	SensitiveHeaders []string `json:"sensitive_headers"` // 敏感请求头列表
	SensitiveFields  []string `json:"sensitive_fields"`  // 敏感字段列表（请求体中）
}

// responseWriter 自定义响应写入器，用于记录响应内容
type responseWriter struct {
	gin.ResponseWriter
	size int
	body *bytes.Buffer
}

func (w *responseWriter) Write(data []byte) (int, error) {
	w.body.Write(data)
	size, err := w.ResponseWriter.Write(data)
	w.size += size
	return size, err
}

func (w *responseWriter) WriteString(s string) (int, error) {
	w.body.WriteString(s)
	size, err := w.ResponseWriter.WriteString(s)
	w.size += size
	return size, err
}

// Middleware 访问日志中间件
type Middleware struct {
	config     *Config
	logger     logger.Logger
	fileLogger *lumberjack.Logger
}

// NewMiddleware 创建访问日志中间件
func NewMiddleware(config *Config, logger logger.Logger) (*Middleware, error) {
	middleware := &Middleware{
		config: config,
		logger: logger,
	}

	// 如果输出到文件，初始化文件日志器
	if config.Output == "file" || config.Output == "both" {
		if err := middleware.initFileLogger(); err != nil {
			return nil, fmt.Errorf("初始化访问日志文件失败: %w", err)
		}
	}

	return middleware, nil
}

// NewEnvironmentMiddleware 根据环境自动创建访问日志中间件
func NewEnvironmentMiddleware(environment string, logConfig config.LogConfig, logger logger.Logger) (*Middleware, error) {
	// 获取跳过路径配置
	skipPathsConfig := CustomSkipPathsConfig()

	var config *Config

	switch environment {
	case "production":
		// 生产环境：启用访问日志，完整记录请求体和响应体
		config = &Config{
			Enable:           true,
			IncludeBody:      true,                  // 生产环境：完整记录
			MaxBodySize:      logConfig.MaxBodySize, // 从配置文件读取
			Format:           "json",
			Output:           "file",
			Filename:         "./storage/logs/access.log",       // 访问日志单独文件
			MaxSize:          logConfig.MaxSize,                 // 从配置文件读取
			MaxAge:           logConfig.MaxAge,                  // 从配置文件读取
			MaxBackups:       logConfig.MaxBackups,              // 从配置文件读取
			Compress:         logConfig.Compress,                // 从配置文件读取
			SkipPaths:        skipPathsConfig.GetAllSkipPaths(), // 使用单独配置文件
			SkipStatuses:     []int{},
			SensitiveHeaders: []string{"Authorization", "Cookie", "X-API-Key"},
			SensitiveFields:  []string{"password", "token", "secret", "key"},
		}
	default: // development
		// 开发环境：启用访问日志，记录请求体和响应体（用于调试）
		config = &Config{
			Enable:           true,
			IncludeBody:      true,                  // 开发环境：也记录请求体和响应体（便于调试）
			MaxBodySize:      logConfig.MaxBodySize, // 从配置文件读取
			Format:           "text",
			Output:           "console",
			Filename:         "./storage/logs/access.log",       // 访问日志单独文件（备用）
			MaxSize:          logConfig.MaxSize,                 // 从配置文件读取
			MaxAge:           logConfig.MaxAge,                  // 从配置文件读取
			MaxBackups:       logConfig.MaxBackups,              // 从配置文件读取
			Compress:         logConfig.Compress,                // 从配置文件读取
			SkipPaths:        skipPathsConfig.GetAllSkipPaths(), // 使用单独配置文件
			SkipStatuses:     []int{200, 304},                   // 开发时跳过成功请求
			SensitiveHeaders: []string{"Authorization", "Cookie"},
			SensitiveFields:  []string{"password", "token"},
		}
	}

	// 支持环境变量覆盖关键配置
	if enable := os.Getenv("ACCESS_LOG_ENABLE"); enable != "" {
		config.Enable = enable == "true"
	}

	return NewMiddleware(config, logger)
}

// initFileLogger 初始化文件日志器
func (m *Middleware) initFileLogger() error {
	// 确保日志目录存在
	logDir := filepath.Dir(m.config.Filename)
	if err := os.MkdirAll(logDir, 0755); err != nil {
		return fmt.Errorf("创建日志目录失败: %w", err)
	}

	// 配置lumberjack日志轮转
	m.fileLogger = &lumberjack.Logger{
		Filename:   m.config.Filename,
		MaxSize:    m.config.MaxSize,
		MaxAge:     m.config.MaxAge,
		MaxBackups: m.config.MaxBackups,
		Compress:   m.config.Compress,
		LocalTime:  true,
	}

	return nil
}

// Handler 中间件处理函数
func (m *Middleware) Handler() gin.HandlerFunc {
	return func(c *gin.Context) {
		// 如果访问日志未启用，直接通过
		if !m.config.Enable {
			c.Next()
			return
		}

		// 检查是否在跳过路径中
		if m.shouldSkipPath(c.Request.URL.Path) {
			c.Next()
			return
		}

		// 记录开始时间
		startTime := time.Now()

		// 包装响应写入器以记录响应内容
		writer := &responseWriter{
			ResponseWriter: c.Writer,
			body:           bytes.NewBuffer([]byte{}),
		}
		c.Writer = writer

		// 记录请求体（如果需要）
		var requestBody string
		var requestSize int64
		if m.config.IncludeBody && (c.Request.Method == "POST" || c.Request.Method == "PUT" || c.Request.Method == "PATCH") {
			requestBody, requestSize = m.readRequestBody(c)
		} else {
			requestSize = c.Request.ContentLength
		}

		// 处理请求
		c.Next()

		// 计算响应时间
		responseTime := time.Since(startTime)

		// 检查是否应该跳过记录（基于状态码）
		if m.shouldSkipStatus(c.Writer.Status()) {
			return
		}

		// 创建访问日志条目
		entry := m.createLogEntry(c, startTime, responseTime, requestSize, writer.size, requestBody, writer.body.String())

		// 写入日志
		m.writeLog(entry)
	}
}

// readRequestBody 读取并过滤请求体
func (m *Middleware) readRequestBody(c *gin.Context) (string, int64) {
	if c.Request.Body == nil {
		return "", 0
	}

	// 读取请求体
	body, err := io.ReadAll(c.Request.Body)
	if err != nil {
		m.logger.Errorf("读取请求体失败: %v", err)
		return "", 0
	}

	// 重新设置body以供后续使用
	c.Request.Body = io.NopCloser(bytes.NewBuffer(body))

	// 检查大小限制
	if len(body) > m.config.MaxBodySize {
		truncated := body[:m.config.MaxBodySize]
		bodyStr := string(truncated) + "...[截断]"
		return m.filterSensitiveData(bodyStr), int64(len(body))
	}

	return m.filterSensitiveData(string(body)), int64(len(body))
}

// filterSensitiveData 过滤敏感信息
func (m *Middleware) filterSensitiveData(data string) string {
	// 简单的敏感信息过滤
	for _, field := range m.config.SensitiveFields {
		// 使用简单的字符串替换来隐藏敏感字段
		if strings.Contains(strings.ToLower(data), strings.ToLower(field)) {
			// 这里可以实现更复杂的JSON字段过滤逻辑
			// 当前简单处理：如果包含敏感字段，标记为已过滤
			return "[包含敏感信息，已过滤]"
		}
	}
	return data
}

// shouldSkipPath 检查是否应该跳过记录该路径
func (m *Middleware) shouldSkipPath(path string) bool {
	for _, skipPath := range m.config.SkipPaths {
		// 支持通配符匹配
		if matched, _ := filepath.Match(skipPath, path); matched {
			return true
		}
		// 支持前缀匹配
		if strings.HasSuffix(skipPath, "*") {
			prefix := strings.TrimSuffix(skipPath, "*")
			if strings.HasPrefix(path, prefix) {
				return true
			}
		}
		// 精确匹配
		if path == skipPath {
			return true
		}
	}
	return false
}

// shouldSkipStatus 检查是否应该跳过记录该状态码
func (m *Middleware) shouldSkipStatus(statusCode int) bool {
	for _, skipStatus := range m.config.SkipStatuses {
		if statusCode == skipStatus {
			return true
		}
	}
	return false
}

// createLogEntry 创建日志条目
func (m *Middleware) createLogEntry(c *gin.Context, startTime time.Time, responseTime time.Duration, requestSize int64, responseSize int, requestBody, responseBody string) *Entry {
	entry := &Entry{
		Timestamp:    startTime.Format("2006-01-02 15:04:05.000"),
		Method:       c.Request.Method,
		Path:         c.Request.URL.Path,
		Query:        c.Request.URL.RawQuery,
		StatusCode:   c.Writer.Status(),
		ResponseTime: formatDuration(responseTime),
		ClientIP:     c.ClientIP(),
		UserAgent:    c.GetHeader("User-Agent"),
		RequestSize:  requestSize,
		ResponseSize: responseSize,
	}

	// 添加请求体和响应体（如果启用）
	if m.config.IncludeBody {
		if requestBody != "" {
			entry.RequestBody = requestBody
		}
		if responseBody != "" && len(responseBody) <= m.config.MaxBodySize {
			entry.ResponseBody = m.filterSensitiveData(responseBody)
		} else if len(responseBody) > m.config.MaxBodySize {
			entry.ResponseBody = responseBody[:m.config.MaxBodySize] + "...[截断]"
		}
	}

	// 获取请求ID
	if requestID, exists := c.Get("request_id"); exists {
		entry.RequestID = fmt.Sprintf("%v", requestID)
	} else if requestID := c.GetHeader("X-Request-ID"); requestID != "" {
		entry.RequestID = requestID
	}

	// 获取用户ID
	if userID, exists := c.Get("user_id"); exists {
		entry.UserID = userID
	}

	// 获取错误信息（如果有）
	if errors := c.Errors; len(errors) > 0 {
		entry.Error = errors.String()
	}

	return entry
}

// writeLog 写入日志
func (m *Middleware) writeLog(entry *Entry) {
	switch m.config.Format {
	case "json":
		m.writeJSONLog(entry)
	case "text":
		m.writeTextLog(entry)
	default:
		m.writeJSONLog(entry)
	}
}

// writeJSONLog 写入JSON格式日志
func (m *Middleware) writeJSONLog(entry *Entry) {
	jsonData, err := json.Marshal(entry)
	if err != nil {
		m.logger.Errorf("序列化访问日志失败: %v", err)
		return
	}

	logLine := string(jsonData) + "\n"
	m.outputLog(logLine)
}

// writeTextLog 写入文本格式日志
func (m *Middleware) writeTextLog(entry *Entry) {
	// 简化的文本格式
	logLine := fmt.Sprintf("[%s] %s %s %s %d %s %s\n",
		entry.Timestamp,
		entry.Method,
		entry.Path,
		entry.ClientIP,
		entry.StatusCode,
		entry.ResponseTime,
		entry.RequestID,
	)

	m.outputLog(logLine)
}

// outputLog 输出日志到指定位置
func (m *Middleware) outputLog(logLine string) {
	switch m.config.Output {
	case "file":
		if m.fileLogger != nil {
			m.fileLogger.Write([]byte(logLine))
		}
	case "console":
		fmt.Print(logLine)
	case "both":
		if m.fileLogger != nil {
			m.fileLogger.Write([]byte(logLine))
		}
		fmt.Print(logLine)
	default:
		// 默认输出到应用日志
		m.logger.Infof("ACCESS: %s", strings.TrimSuffix(logLine, "\n"))
	}
}

// formatDuration 格式化持续时间
func formatDuration(d time.Duration) string {
	if d < time.Microsecond {
		return fmt.Sprintf("%.0fns", float64(d.Nanoseconds()))
	} else if d < time.Millisecond {
		return fmt.Sprintf("%.1fµs", float64(d.Nanoseconds())/1000)
	} else if d < time.Second {
		return fmt.Sprintf("%.1fms", float64(d.Nanoseconds())/1000000)
	} else {
		return fmt.Sprintf("%.3fs", d.Seconds())
	}
}

// Close 关闭中间件
func (m *Middleware) Close() error {
	if m.fileLogger != nil {
		return m.fileLogger.Close()
	}
	return nil
}
